/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "libzlang_network.h"

#include "fasterhttp.h"

#define DEBUG_PRINT_HTTP_REQUEST_LEN_MAX	1000
#define DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX	2000

struct ZlangDirectProperty_httpclient
{
	struct ZlangTcp		tcp ;
	SSL_CTX			*ssl_ctx ;
	SSL			*ssl ;
	struct HttpEnv		*http_env ;
	struct ZlangObject	*cookies_list_obj ;
} ;

static struct ZlangDirectFunctions direct_funcs_httpclient ;

#define PROTOCOL_HTTP		0
#define PROTOCOL_HTTPS		1

static int AnalyseUrl( struct ZlangRuntime *rt , char **http_url , int *protocol , char **domain_name , int *port , char **http_uri )
{
	char		*p1 = NULL ;
	char		*p2 = NULL ;
	
	(*http_url) = ZLSTRDUP( (*http_url) ) ;
	if( (*http_url) == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "alloc failed[%d]" , errno )
		return -1;
	}
	
	p1 = strstr( (*http_url) , "://" ) ;
	if( p1 == NULL )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "url[%s] invalid" , (*http_url) )
		ZLFREE( (*http_url) );
		return -2;
	}
	(*p1) = '\0' ;
	if( STRCMP( (*http_url) , == , "http" ) )
	{
		(*protocol) = PROTOCOL_HTTP ;
	}
	else if( STRCMP( (*http_url) , == , "https" ) )
	{
		(*protocol) = PROTOCOL_HTTPS ;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "protocol[%s] invalid" , (*http_url) )
		ZLFREE( (*http_url) );
		return -3;
	}
	(*domain_name) = p1 + 3 ;
	p2 = strpbrk( (*domain_name) , ":/" ) ;
	if( p2 == NULL )
	{
		if( (*protocol) == PROTOCOL_HTTPS )
			(*port) = 443 ;
		else
			(*port) = 80 ;
		(*http_uri) = "" ;
	}
	else if( (*p2) == ':' )
	{
		(*p2) = '\0' ;
		(*port) = atoi( p2 + 1 ) ;
		p2 = strchr( p2+1 , '/' ) ;
		if( p2 == NULL )
		{
			(*http_uri) = "" ;
		}
		else
		{
			(*http_uri) = p2 + 1 ;
		}
	}
	else
	{
		(*p2) = '\0' ;
		if( (*protocol) == PROTOCOL_HTTPS )
			(*port) = 443 ;
		else
			(*port) = 80 ;
		(*http_uri) = p2 + 1 ;
	}
	
	return 0;
}

static int AssembleHttpRequestHeaders( struct ZlangRuntime *rt , struct ZlangObject *http_req_header_list , struct HttpBuffer *http_req_buf )
{
	struct ZlangObject	*list_node_obj = NULL ;
	struct ZlangObject	*http_header_obj = NULL ;
	char			*http_header = NULL ;
	int			http_header_len ;
	int			nret = 0 ;
	
	if( IsObjectPropertiesEntityNotNull(http_req_header_list) )
	{
		CallRuntimeFunction_list_GetHead( rt , http_req_header_list , & list_node_obj );
		for( ; ; )
		{
			if( CallRuntimeFunction_list_node_IsTravelOver( rt , list_node_obj ) )
				break;
			
			CallRuntimeFunction_list_node_GetMember( rt , list_node_obj , & http_header_obj );
			CallRuntimeFunction_string_GetStringValue( rt , http_header_obj , & http_header , & http_header_len );
			
			if( http_header_len > 0 )
			{
				nret = StrcatfHttpBuffer( http_req_buf , "%.*s\r\n" , http_header_len,http_header ) ;
				if( nret )
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcatfHttpBuffer http header failed[%d]" , nret )
					return -2;
				}
				else
				{
					TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "set http request header[%.*s]" , http_header_len,http_header )
				}
			}
			
			CallRuntimeFunction_list_node_TravelNext( rt , list_node_obj );
		}
	}
	
	return 0;
}

static int AssembleHttpRequestBody( struct ZlangRuntime *rt , char *http_req_body , int http_req_body_len , struct HttpBuffer *http_req_buf )
{
	int		nret = 0 ;
	
	if( http_req_body )
	{
		nret = StrcatfHttpBuffer( http_req_buf , "Content-length: %d\r\n\r\n%.*s" , http_req_body_len , http_req_body_len,http_req_body ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcatfHttpBuffer http body failed" )
			return -1;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcatfHttpBuffer http body ok" )
		}
	}
	else
	{
		nret = StrcatfHttpBuffer( http_req_buf , "\r\n" ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcatfHttpBuffer separator line failed" )
			return -2;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcatfHttpBuffer separator line  ok" )
		}
	}
	
	return 0;
}

static int ExtractHttpResponseHeaders( struct ZlangRuntime *rt , struct HttpEnv *http_env , struct ZlangObject *header_map , struct ZlangObject *cookies_list )
{
	struct HttpHeader	*http_header = NULL ;
	char			*http_header_name = NULL ;
	int			http_header_name_len ;
	struct ZlangObject	*http_header_name_obj = NULL ;
	char			*http_header_value = NULL ;
	int			http_header_value_len ;
	struct ZlangObject	*http_header_value_obj = NULL ;
	int			nret = 0 ;
	
	if( IsObjectPropertiesEntityNotNull(header_map) )
	{
		CallRuntimeFunction_map_RemoveAll( rt , header_map );
		CallRuntimeFunction_list_RemoveAll( rt , cookies_list );
		http_header = NULL ;
		for( ; ; )
		{
			http_header = TravelHttpHeaderPtr( http_env , http_header ) ;
			if( http_header == NULL )
				break;
			
			http_header_name = GetHttpHeaderNamePtr( http_header , & http_header_name_len ) ;
			http_header_value = GetHttpHeaderValuePtr( http_header , & http_header_value_len ) ;
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "get http response header[%.*s][%.*s]" , http_header_name_len,http_header_name , http_header_value_len,http_header_value )
			
			http_header_name_obj = CloneStringObject( rt , NULL ) ;
			if( http_header_name_obj == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneStringObject failed" )
				return -11;
			}
			
			nret = CallRuntimeFunction_string_SetStringValue( rt , http_header_name_obj , http_header_name , http_header_name_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_string_SetStringValue http response header name failed" )
				DestroyObject( rt , http_header_name_obj );
				return -12;
			}
			
			http_header_value_obj = CloneStringObject( rt , NULL ) ;
			if( http_header_value_obj == NULL )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneStringObject failed" )
				DestroyObject( rt , http_header_name_obj );
				return -31;
			}
			
			nret = CallRuntimeFunction_string_SetStringValue( rt , http_header_value_obj , http_header_value , http_header_value_len ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_string_SetStringValue http response header value failed" )
				DestroyObject( rt , http_header_name_obj );
				DestroyObject( rt , http_header_value_obj );
				return -32;
			}
			
			nret = CallRuntimeFunction_map_Put( rt , header_map , http_header_name_obj , http_header_value_obj , NULL ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_map_Put failed[%d]" , nret )
			}
			else
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_map_Put ok" )
			}
			
			DestroyObject( rt , http_header_name_obj );
			DestroyObject( rt , http_header_value_obj );
			
			if( STRNICMP( http_header_name , == , HTTP_HEADER_SET_COOKIE , sizeof(HTTP_HEADER_SET_COOKIE)-1 ) )
			{
				char			*p = http_header_value ;
				char			*overend = http_header_value+http_header_value_len ;
				char			*cookie = NULL ;
				struct ZlangObject	*cookie_obj = NULL ;
				
				for( ; ; )
				{
					for( ; p < overend ; p++ )
					{
						if( (*p) == ' ' )
							;
						else
							break;
					}
					if( p >= overend )
						break;
					
					cookie = p ;
					
					for( ; p < overend ; p++ )
					{
						if( (*p) == ';' )
							break;
					}
					
					if( STRNICMP( cookie , == , "path=" , 5 ) || STRNICMP( cookie , == , "expires=" , 8 ) || STRNICMP( cookie , == , "domain=" , 7 ) || STRNICMP( cookie , == , "secure" , 6 ) || STRNICMP( cookie , == , "HttpOnly" , 8 ) || STRNICMP( cookie , == , "SameSite=" , 9 ) )
						break;
					
					cookie_obj = CloneStringObject( rt , NULL ) ;
					if( cookie_obj == NULL )
					{
						TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CloneStringObject failed" )
						return GetRuntimeErrorNo(rt);
					}
					
					nret = CallRuntimeFunction_string_SetStringValue( rt , cookie_obj , cookie , p-cookie ) ;
					if( nret )
					{
						TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_string_SetStringValue failed" )
						return nret;
					}
					
					nret = CallRuntimeFunction_list_AddTail( rt , cookies_list , cookie_obj , NULL ) ;
					if( nret )
					{
						TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_list_AddTail failed" )
						return nret;
					}
					
					DestroyObject( rt , cookie_obj );
					
					if( p >= overend )
						break;
					
					p++;
				}
			}
		}
	}
	
	return 0;
}

static int ExtractHttpResponseBody( struct ZlangRuntime *rt , struct HttpEnv *http_env , struct ZlangObject *http_rsp_body_obj )
{
	char		*http_rsp_body = NULL ;
	int		http_rsp_body_len ;
	int		nret = 0 ;
	
	if( IsObjectPropertiesEntityNotNull(http_rsp_body_obj) )
	{
		http_rsp_body = GetHttpBodyPtr( http_env , & http_rsp_body_len ) ;
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "get http response body[%d][%.*s%s]" , http_rsp_body_len , (http_rsp_body_len>DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX?DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX:http_rsp_body_len),http_rsp_body , (http_rsp_body_len>DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX?"...":"") )
		nret = CallRuntimeFunction_string_SetStringValue( rt , http_rsp_body_obj , http_rsp_body , http_rsp_body_len ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "CallRuntimeFunction_string_SetStringValue http response body failed" )
			return -1;
		}
	}
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_httpclient_Get_string_list_map_string;
int ZlangInvokeFunction_httpclient_Get_string_list_map_string( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_httpclient	*httpclient_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*in2 = GetInputParameterInLocalObjectStack(rt,2) ;
	struct ZlangObject			*in3 = GetInputParameterInLocalObjectStack(rt,3) ;
	struct ZlangObject			*in4 = GetInputParameterInLocalObjectStack(rt,4) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	char					*http_url = NULL ;
	
	char					*http_url_bak = NULL ;
	int					protocol = 0 ;
	char					*domain_name = NULL ;
	int32_t					port ;
	char					*http_uri = NULL ;
	
	size_t					ip_count ;
	size_t					ip_index ;
	struct Ipv4DnsResolution		*ip_array = NULL ;
	
	struct HttpBuffer			*http_req_buf = NULL ;
	struct HttpBuffer			*http_rsp_buf = NULL ;
	
	char					*http_req = NULL ;
	int					http_req_len ;
	char					*http_rsp = NULL ;
	int					http_rsp_len ;
	
	int					http_status_code ;
	
	int					nret = 0 ;
	
	CallRuntimeFunction_string_GetStringValue( rt , in1 , & http_url , NULL );
	
	http_url_bak = http_url ;
	nret = AnalyseUrl( rt , & http_url , & protocol , & domain_name , & port , & http_uri ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AnalyseUrl url[%s] failed[%d]" , http_url_bak , nret )
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -11 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AnalyseUrl url[%s] ok , protocol[%d] domain_name[%s] port[%d] http_uri[%s]" , http_url_bak , protocol , domain_name , port , http_uri )
	}
	
	nret = GetIpByDomainName( domain_name , & ip_count , & ip_array ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "GetIpByDomainName[%s] failed[%d]" , domain_name , nret )
		ZLFREE( http_url );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -21 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "GetIpByDomainName[%s] ok" , domain_name )
		for( ip_index = 0 ; ip_index < ip_count ; ip_index++ )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "domain_name[%s] -> ip[%s]" , domain_name , ip_array[ip_index].ip_str )
		}
	}
	
	ResetHttpEnv( httpclient_direct_prop->http_env );
	
	http_req_buf = GetHttpRequestBuffer( httpclient_direct_prop->http_env ) ;
	nret = StrcpyfHttpBuffer( http_req_buf , "GET /%s HTTP/1.0\r\n" , http_uri ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcpyfHttpBuffer first line failed" )
		ZLFREE( http_url );
		ZLFREE( ip_array );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -31 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcpyfHttpBuffer first line ok" )
	}
	
	ZLFREE( http_url );
	
	nret = AssembleHttpRequestHeaders( rt , in2 , http_req_buf ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AssembleHttpRequestHeaders failed[%d]" , nret )
		ZLFREE( ip_array );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -32 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AssembleHttpRequestHeaders ok" )
	}
	
	nret = AssembleHttpRequestBody( rt , NULL , 0 , http_req_buf ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AssembleHttpRequestBody failed[%d]" , nret )
		ZLFREE( ip_array );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -4 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AssembleHttpRequestBody ok" )
	}
	
	nret = ZlangConnectTcp( rt , & (httpclient_direct_prop->tcp) , ip_array[0].ip_str , port ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ZlangConnectTcp[%s:%d] failed[%d]" , ip_array[0].ip_str,port , nret )
		ZLFREE( ip_array );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -41 );
		return 0;
	}
	
	ZLFREE( ip_array );
	
	if( protocol == PROTOCOL_HTTPS )
	{
		httpclient_direct_prop->ssl = SSL_new( httpclient_direct_prop->ssl_ctx ) ;
		if( httpclient_direct_prop->ssl == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "SSL_new failed" )
			ZlangCloseTcp( rt , & (httpclient_direct_prop->tcp) );
			CallRuntimeFunction_int_SetIntValue( rt , out1 , -51 );
			return 0;
		}
		
		SSL_set_fd( httpclient_direct_prop->ssl , ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) );
		
		nret = SSL_connect( httpclient_direct_prop->ssl ) ;
		if( nret == -1 )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "SSL_connect failed , error[%d][%s]" , SSL_get_error(httpclient_direct_prop->ssl,0) , ERR_error_string(ERR_get_error(),NULL) )
			SSL_shutdown( httpclient_direct_prop->ssl ); SSL_free( httpclient_direct_prop->ssl );
			ZlangCloseTcp( rt , & (httpclient_direct_prop->tcp) );
			CallRuntimeFunction_int_SetIntValue( rt , out1 , -52 );
			return 0;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "SSL_connect ok" )
		}
	}
	
	http_req = GetHttpBufferBase( http_req_buf , & http_req_len ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "request http[%d][%.*s%s]" , http_req_len , (http_req_len>DEBUG_PRINT_HTTP_REQUEST_LEN_MAX?DEBUG_PRINT_HTTP_REQUEST_LEN_MAX:http_req_len),http_req , (http_req_len>DEBUG_PRINT_HTTP_REQUEST_LEN_MAX?"...":"") )
	
	if( protocol == PROTOCOL_HTTP )
		nret = RequestHttp( ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) , NULL , httpclient_direct_prop->http_env ) ;
	else
		nret = RequestHttp( ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) , httpclient_direct_prop->ssl , httpclient_direct_prop->http_env ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "RequestHttp failed[%d]" , nret )
		if( protocol == PROTOCOL_HTTPS ) { SSL_shutdown( httpclient_direct_prop->ssl ); SSL_free( httpclient_direct_prop->ssl ); httpclient_direct_prop->ssl = NULL ; }
		ZlangCloseTcp( rt , & (httpclient_direct_prop->tcp) );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -42 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "RequestHttp ok" )
	}
	
	if( protocol == PROTOCOL_HTTPS ) { SSL_shutdown( httpclient_direct_prop->ssl ); SSL_free( httpclient_direct_prop->ssl ); httpclient_direct_prop->ssl = NULL ; }
	ZlangCloseTcp( rt , & (httpclient_direct_prop->tcp) ); ZLANG_TCP_SET_SOCK( httpclient_direct_prop->tcp , -1 );
	
	http_rsp_buf = GetHttpResponseBuffer( httpclient_direct_prop->http_env ) ;
	http_rsp = GetHttpBufferBase( http_rsp_buf , & http_rsp_len ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "response http[%d][%.*s%s]" , http_rsp_len , (http_rsp_len>DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX?DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX:http_rsp_len),http_rsp , (http_rsp_len>DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX?"...":"") )
	
	http_status_code = GetHttpStatusCode( httpclient_direct_prop->http_env ) ;
	
	nret = ExtractHttpResponseHeaders( rt , httpclient_direct_prop->http_env , in3 , httpclient_direct_prop->cookies_list_obj ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ExtractHttpResponseHeaders failed[%d]" , nret )
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -61 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ExtractHttpResponseHeaders ok" )
	}
	
	nret = ExtractHttpResponseBody( rt , httpclient_direct_prop->http_env , in4 ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ExtractHttpResponseBody failed[%d]" , nret )
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -62 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ExtractHttpResponseBody ok" )
	}
	
	CallRuntimeFunction_int_SetIntValue( rt , out1 , http_status_code );
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_httpclient_Post_string_list_map_string;
int ZlangInvokeFunction_httpclient_Post_string_list_map_string( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_httpclient	*httpclient_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*in2 = GetInputParameterInLocalObjectStack(rt,2) ;
	struct ZlangObject			*in3 = GetInputParameterInLocalObjectStack(rt,3) ;
	struct ZlangObject			*in4 = GetInputParameterInLocalObjectStack(rt,4) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	char					*http_url = NULL ;
	char					*http_req_body = NULL ;
	int32_t					http_req_body_len ;
	
	char					*http_url_bak = NULL ;
	int					protocol = 0 ;
	char					*domain_name = NULL ;
	int32_t					port ;
	char					*http_uri = NULL ;
	
	size_t					ip_count ;
	size_t					ip_index ;
	struct Ipv4DnsResolution		*ip_array = NULL ;
	
	struct HttpBuffer			*http_req_buf = NULL ;
	struct HttpBuffer			*http_rsp_buf = NULL ;
	
	char					*http_req = NULL ;
	int					http_req_len ;
	char					*http_rsp = NULL ;
	int					http_rsp_len ;
	
	int					http_status_code ;
	
	int					nret = 0 ;
	
	CallRuntimeFunction_string_GetStringValue( rt , in1 , & http_url , NULL );
	CallRuntimeFunction_string_GetStringValue( rt , in4 , & http_req_body , & http_req_body_len );
	
	http_url_bak = http_url ;
	nret = AnalyseUrl( rt , & http_url , & protocol , & domain_name , & port , & http_uri ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AnalyseUrl url[%s] failed[%d]" , http_url_bak , nret )
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -11 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AnalyseUrl url[%s] ok , protocol[%d] domain_name[%s] port[%d] http_uri[%s]" , http_url_bak , protocol , domain_name , port , http_uri )
	}
	
	nret = GetIpByDomainName( domain_name , & ip_count , & ip_array ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "GetIpByDomainName[%s] failed[%d]" , domain_name , nret )
		ZLFREE( http_url );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -21 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "GetIpByDomainName[%s] ok" , domain_name )
		for( ip_index = 0 ; ip_index < ip_count ; ip_index++ )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "domain_name[%s] -> ip[%s]" , domain_name , ip_array[ip_index].ip_str )
		}
	}
	
	ResetHttpEnv( httpclient_direct_prop->http_env );
	
	http_req_buf = GetHttpRequestBuffer( httpclient_direct_prop->http_env ) ;
	nret = StrcpyfHttpBuffer( http_req_buf , "POST /%s HTTP/1.0\r\n" , http_uri ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcpyfHttpBuffer first line failed" )
		ZLFREE( http_url );
		ZLFREE( ip_array );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -31 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcpyfHttpBuffer first line ok" )
	}
	
	ZLFREE( http_url );
	
	nret = AssembleHttpRequestHeaders( rt , in2 , http_req_buf ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AssembleHttpRequestHeaders failed[%d]" , nret )
		ZLFREE( ip_array );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -32 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AssembleHttpRequestHeaders ok" )
	}
	
	nret = AssembleHttpRequestBody( rt , http_req_body , http_req_body_len , http_req_buf ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AssembleHttpRequestBody failed[%d]" , nret )
		ZLFREE( ip_array );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -4 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "AssembleHttpRequestBody ok" )
	}
	
	nret = ZlangConnectTcp( rt , & (httpclient_direct_prop->tcp) , ip_array[0].ip_str , port ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ZlangConnectTcp[%s:%d] failed[%d]" , ip_array[0].ip_str,port , nret )
		ZLFREE( ip_array );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -41 );
		return 0;
	}
	
	ZLFREE( ip_array );
	
	if( protocol == PROTOCOL_HTTPS )
	{
		httpclient_direct_prop->ssl = SSL_new( httpclient_direct_prop->ssl_ctx ) ;
		if( httpclient_direct_prop->ssl == NULL )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "SSL_new failed" )
			ZlangCloseTcp( rt , & (httpclient_direct_prop->tcp) );
			CallRuntimeFunction_int_SetIntValue( rt , out1 , -51 );
			return 0;
		}
		
		SSL_set_fd( httpclient_direct_prop->ssl , ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) );
		
		nret = SSL_connect( httpclient_direct_prop->ssl ) ;
		if( nret == -1 )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "SSL_connect failed , error[%d][%s]" , SSL_get_error(httpclient_direct_prop->ssl,0) , ERR_error_string(ERR_get_error(),NULL) )
			SSL_shutdown( httpclient_direct_prop->ssl ); SSL_free( httpclient_direct_prop->ssl );
			ZlangCloseTcp( rt , & (httpclient_direct_prop->tcp) );
			CallRuntimeFunction_int_SetIntValue( rt , out1 , -52 );
			return 0;
		}
		else
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "SSL_connect ok" )
		}
	}
	
	http_req = GetHttpBufferBase( http_req_buf , & http_req_len ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "request http[%d][%.*s%s]" , http_req_len , (http_req_len>DEBUG_PRINT_HTTP_REQUEST_LEN_MAX?DEBUG_PRINT_HTTP_REQUEST_LEN_MAX:http_req_len),http_req , (http_req_len>DEBUG_PRINT_HTTP_REQUEST_LEN_MAX?"...":"") )
	
	if( protocol == PROTOCOL_HTTP )
		nret = RequestHttp( ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) , NULL , httpclient_direct_prop->http_env ) ;
	else
		nret = RequestHttp( ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) , httpclient_direct_prop->ssl , httpclient_direct_prop->http_env ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "RequestHttp failed[%d]" , nret )
		if( protocol == PROTOCOL_HTTPS ) { SSL_shutdown( httpclient_direct_prop->ssl ); SSL_free( httpclient_direct_prop->ssl ); httpclient_direct_prop->ssl = NULL ; }
		ZlangCloseTcp( rt , & (httpclient_direct_prop->tcp) );
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -42 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "RequestHttp ok" )
	}
	
	if( protocol == PROTOCOL_HTTPS ) { SSL_shutdown( httpclient_direct_prop->ssl ); SSL_free( httpclient_direct_prop->ssl ); httpclient_direct_prop->ssl = NULL ; }
	ZlangCloseTcp( rt , & (httpclient_direct_prop->tcp) ); ZLANG_TCP_SET_SOCK( httpclient_direct_prop->tcp , -1 );
	
	http_rsp_buf = GetHttpResponseBuffer( httpclient_direct_prop->http_env ) ;
	http_rsp = GetHttpBufferBase( http_rsp_buf , & http_rsp_len ) ;
	TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "response http[%d][%.*s%s]" , http_rsp_len , (http_rsp_len>DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX?DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX:http_rsp_len),http_rsp , (http_rsp_len>DEBUG_PRINT_HTTP_RESPONSE_LEN_MAX?"...":"") )
	
	http_status_code = GetHttpStatusCode( httpclient_direct_prop->http_env ) ;
	
	nret = ExtractHttpResponseHeaders( rt , httpclient_direct_prop->http_env , in3 , httpclient_direct_prop->cookies_list_obj ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ExtractHttpResponseHeaders failed[%d]" , nret )
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -61 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ExtractHttpResponseHeaders ok" )
	}
	
	nret = ExtractHttpResponseBody( rt , httpclient_direct_prop->http_env , in4 ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ExtractHttpResponseBody failed[%d]" , nret )
		CallRuntimeFunction_int_SetIntValue( rt , out1 , -62 );
		return 0;
	}
	else
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "ExtractHttpResponseBody ok" )
	}
	
	CallRuntimeFunction_int_SetIntValue( rt , out1 , http_status_code );
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_httpclient_GetCookies;
int ZlangInvokeFunction_httpclient_GetCookies( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_httpclient	*httpclient_direct_prop = GetObjectDirectProperty(obj) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	
	ReferObject( rt , out1 , httpclient_direct_prop->cookies_list_obj );
	
	return 0;
}

ZlangInvokeFunction ZlangInvokeFunction_httpclient_FormatCookies;
int ZlangInvokeFunction_httpclient_FormatCookies( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangObject			*in1 = GetInputParameterInLocalObjectStack(rt,1) ;
	struct ZlangObject			*out1 = GetOutputParameterInLocalObjectStack(rt,1) ;
	struct HttpBuffer			*cookies_buf = NULL ;
	struct ZlangObject			*list_node_obj = NULL ;
	struct ZlangObject			*http_header_obj = NULL ;
	char					*http_header = NULL ;
	int					http_header_len ;
	char					*cookies_str = NULL ;
	int					cookies_str_len ;
	int					nret = 0 ;
	
	cookies_buf = AllocHttpBuffer( 64 ) ;
	if( cookies_buf == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "AllocHttpBuffer failed" )
		return ZLANG_FATAL_INVOKE_METHOD_RETURN;
	}
	
	nret = StrcatfHttpBuffer( cookies_buf , HTTP_HEADER_COOKIE": " ) ;
	if( nret )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcatfHttpBuffer failed[%d]" , nret )
		FreeHttpBuffer( cookies_buf );
		return ZLANG_FATAL_INVOKE_METHOD_RETURN;
	}
	
	CallRuntimeFunction_list_GetHead( rt , in1 , & list_node_obj );
	for( ; ; )
	{
		if( CallRuntimeFunction_list_node_IsTravelOver( rt , list_node_obj ) )
			break;
		
		CallRuntimeFunction_list_node_GetMember( rt , list_node_obj , & http_header_obj );
		CallRuntimeFunction_string_GetStringValue( rt , http_header_obj , & http_header , & http_header_len );
		
		if( http_header_len > 0 )
		{
			nret = StrcatfHttpBuffer( cookies_buf , "%.*s" , http_header_len,http_header ) ;
			if( nret )
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcatfHttpBuffer http header failed[%d]" , nret )
				FreeHttpBuffer( cookies_buf );
				return ZLANG_FATAL_INVOKE_METHOD_RETURN;
			}
			else
			{
				TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "format one cookie[%.*s]" , http_header_len,http_header )
			}
		}
		
		CallRuntimeFunction_list_node_TravelNext( rt , list_node_obj );
		
		nret = StrcatfHttpBuffer( cookies_buf , "; " ) ;
		if( nret )
		{
			TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "StrcatfHttpBuffer http header failed[%d]" , nret )
			FreeHttpBuffer( cookies_buf );
			return ZLANG_FATAL_INVOKE_METHOD_RETURN;
		}
		
		if( CallRuntimeFunction_list_node_IsTravelOver( rt , list_node_obj ) )
			break;
	}
	
	cookies_str = GetHttpBufferBase( cookies_buf , & cookies_str_len ) ;
	CallRuntimeFunction_string_SetStringValue( rt , out1 , cookies_str , cookies_str_len );
	
	FreeHttpBuffer( cookies_buf );
	
	return 0;
}

ZlangCreateDirectPropertyFunction ZlangCreateDirectProperty_httpclient;
void *ZlangCreateDirectProperty_httpclient( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_httpclient	*httpclient_direct_prop = NULL ;
	static unsigned char			is_ssl_init = 0 ;
	
	httpclient_direct_prop = (struct ZlangDirectProperty_httpclient *)ZLMALLOC( sizeof(struct ZlangDirectProperty_httpclient) ) ;
	if( httpclient_direct_prop == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_ALLOC , "alloc memory for entity" )
		return NULL;
	}
	memset( httpclient_direct_prop , 0x00 , sizeof(struct ZlangDirectProperty_httpclient) );
	
	ZLANG_TCP_SET_SOCK( httpclient_direct_prop->tcp , -1 );
	ZLANG_TCP_SET_CONNECTINGTIMEOUT( httpclient_direct_prop->tcp , -1 );
	ZLANG_TCP_SET_DATATRANSMISSIONTIMEOUT( httpclient_direct_prop->tcp , -1 );
	
	if( is_ssl_init == 0 )
	{
		SSL_library_init();
		OpenSSL_add_all_algorithms();
		SSL_load_error_strings();
		SSLeay_add_ssl_algorithms();
		ERR_load_BIO_strings();
		
		is_ssl_init = 1 ;
	}
	
	httpclient_direct_prop->ssl_ctx = SSL_CTX_new( SSLv23_client_method() ) ;
	if( httpclient_direct_prop->ssl_ctx == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INVOKE_METHOD_RETURN , "CreateHttpEnv failed" )
		return NULL;
	}
	
	httpclient_direct_prop->http_env = CreateHttpEnv() ;
	if( httpclient_direct_prop->http_env == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INVOKE_METHOD_RETURN , "CreateHttpEnv failed" )
		return NULL;
	}
	
	httpclient_direct_prop->cookies_list_obj = CloneListObject( rt , NULL ) ;
	if( httpclient_direct_prop->cookies_list_obj == NULL )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_INVOKE_METHOD_RETURN , "CreateHttpEnv failed" )
		return NULL;
	}
	
	return httpclient_direct_prop;
}

ZlangDestroyDirectPropertyFunction ZlangDestroyDirectProperty_httpclient;
void ZlangDestroyDirectProperty_httpclient( struct ZlangRuntime *rt , struct ZlangObject *obj )
{
	struct ZlangDirectProperty_httpclient	*httpclient_direct_prop = GetObjectDirectProperty(obj) ;
	
	/* ZLFREE ssl ctx */
	if( httpclient_direct_prop->ssl_ctx )
	{
		SSL_CTX_free( httpclient_direct_prop->ssl_ctx ); httpclient_direct_prop->ssl_ctx = NULL ;
	}
	
	/* destroy httpclient env */
	if( httpclient_direct_prop->http_env )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "DestroyHttpEnv" )
		DestroyHttpEnv( httpclient_direct_prop->http_env ); httpclient_direct_prop->http_env = NULL ;
	}
	
	/* destroy cookies list */
	if( httpclient_direct_prop->cookies_list_obj )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "DestroyObject cookies_list" )
		DestroyObject( rt , httpclient_direct_prop->cookies_list_obj ); httpclient_direct_prop->cookies_list_obj = NULL ;
	}
	
	/* close sock */
	if( ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) >= 0 )
	{
		TEST_RUNTIME_DEBUG_THEN_PRINT( rt , "close sock[%d]" , ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) )
		close( ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) ); ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) = -1 ;
	}
	
	ZLFREE( httpclient_direct_prop );
	
	return;
}

ZlangToStringFunction ZlangToString_httpclient;
int ZlangToString_httpclient( struct ZlangRuntime *rt , struct ZlangObject *obj , struct ZlangObject **tostr_obj )
{
	struct ZlangDirectProperty_httpclient	*httpclient_direct_prop = GetObjectDirectProperty(obj) ;
	char					buf[ 1+10+1+40+1+10 + 1 ] ;
	int32_t					buf_len ;
	
	memset( buf , 0x00 , sizeof(buf) );
	buf_len = snprintf( buf , sizeof(buf)-1 , "#%d#%s:%"PRIi32 , ZLANG_TCP_GET_SOCK(httpclient_direct_prop->tcp) , ZLANG_TCP_GET_IP(httpclient_direct_prop->tcp) , ZLANG_TCP_GET_PORT(httpclient_direct_prop->tcp) ) ;
	CallRuntimeFunction_string_SetStringValue( rt , (*tostr_obj) , buf , buf_len );
	
	return 0;
}

ZlangSummarizeDirectPropertySizeFunction ZlangSummarizeDirectPropertySize_httpclient;
void ZlangSummarizeDirectPropertySize_httpclient( struct ZlangRuntime *rt , struct ZlangObject *obj , size_t *summarized_obj_size , size_t *summarized_direct_prop_size )
{
	struct ZlangDirectProperty_httpclient	*httpclient_direct_prop = GetObjectDirectProperty(obj) ;
	
	if( httpclient_direct_prop->cookies_list_obj )
		SummarizeObjectSize( rt , httpclient_direct_prop->cookies_list_obj , summarized_obj_size , summarized_direct_prop_size );
	
	SUMMARIZE_SIZE( summarized_direct_prop_size , sizeof(struct ZlangDirectProperty_httpclient) )
	
	return;
}

static struct ZlangDirectFunctions direct_funcs_httpclient =
	{
		ZLANG_OBJECT_httpclient , /* char *ancestor_name */
		
		ZlangCreateDirectProperty_httpclient , /* ZlangCreateDirectPropertyFunction *create_entity_func */
		ZlangDestroyDirectProperty_httpclient , /* ZlangDestroyDirectPropertyFunction *destroy_entity_func */
		
		NULL , /* ZlangFromCharPtrFunction *from_char_ptr_func */
		ZlangToString_httpclient , /* ZlangToStringFunction *to_string_func */
		NULL , /* ZlangFromDataPtrFunction *from_data_ptr_func */
		NULL , /* ZlangGetDataPtrFunction *get_data_ptr_func */
		
		NULL , /* ZlangOperatorFunction *oper_PLUS_func */
		NULL , /* ZlangOperatorFunction *oper_MINUS_func */
		NULL , /* ZlangOperatorFunction *oper_MUL_func */
		NULL , /* ZlangOperatorFunction *oper_DIV_func */
		NULL , /* ZlangOperatorFunction *oper_MOD_func */
		
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_NEGATIVE_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_NOT_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_BIT_REVERSE_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_PLUS_PLUS_func */
		NULL , /* ZlangUnaryOperatorFunction *unaryoper_MINUS_MINUS_func */
		
		NULL , /* ZlangCompareFunction *comp_EGUAL_func */
		NULL , /* ZlangCompareFunction *comp_NOTEGUAL_func */
		NULL , /* ZlangCompareFunction *comp_LT_func */
		NULL , /* ZlangCompareFunction *comp_LE_func */
		NULL , /* ZlangCompareFunction *comp_GT_func */
		NULL , /* ZlangCompareFunction *comp_GE_func */
		
		NULL , /* ZlangLogicFunction *logic_AND_func */
		NULL , /* ZlangLogicFunction *logic_OR_func */
		
		NULL , /* ZlangLogicFunction *bit_AND_func */
		NULL , /* ZlangLogicFunction *bit_XOR_func */
		NULL , /* ZlangLogicFunction *bit_OR_func */
		NULL , /* ZlangLogicFunction *bit_MOVELEFT_func */
		NULL , /* ZlangLogicFunction *bit_MOVERIGHT_func */
		
		ZlangSummarizeDirectPropertySize_httpclient , /* ZlangSummarizeDirectPropertySizeFunction *summarize_direct_prop_size_func */
	} ;

ZlangImportObjectFunction ZlangImportObject_httpclient;
struct ZlangObject *ZlangImportObject_httpclient( struct ZlangRuntime *rt )
{
	struct ZlangObject	*obj = NULL ;
	struct ZlangFunction	*func = NULL ;
	int			nret = 0 ;
	
	nret = ImportObject( rt , & obj , ZLANG_OBJECT_httpclient , & direct_funcs_httpclient , sizeof(struct ZlangDirectFunctions) , NULL ) ;
	if( nret )
	{
		SET_RUNTIME_ERROR( rt , RUNTIME_ERROR , ZLANG_ERROR_LINK_FUNC_TO_ENTITY , "import object to global objects heap" )
		return NULL;
	}
	
	/* httpclient.Get(string,list,map,string) */
	func = AddFunctionAndParametersInObject( rt , obj , "Get" , "Get(string,list,map,string)" , ZlangInvokeFunction_httpclient_Get_string_list_map_string , ZLANG_OBJECT_int , ZLANG_OBJECT_string,NULL , ZLANG_OBJECT_list,NULL , ZLANG_OBJECT_map,NULL , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* httpclient.Post(string,list,map,string) */
	func = AddFunctionAndParametersInObject( rt , obj , "Post" , "Post(string,list,map,string)" , ZlangInvokeFunction_httpclient_Post_string_list_map_string , ZLANG_OBJECT_int , ZLANG_OBJECT_string,NULL , ZLANG_OBJECT_list,NULL , ZLANG_OBJECT_map,NULL , ZLANG_OBJECT_string,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* httpclient.GetCookies() */
	func = AddFunctionAndParametersInObject( rt , obj , "GetCookies" , "GetCookies()" , ZlangInvokeFunction_httpclient_GetCookies , ZLANG_OBJECT_list , NULL ) ;
	if( func == NULL )
		return NULL;
	
	/* httpclient.FormatCookies() */
	func = AddFunctionAndParametersInObject( rt , obj , "FormatCookies" , "FormatCookies(list)" , ZlangInvokeFunction_httpclient_FormatCookies , ZLANG_OBJECT_string , ZLANG_OBJECT_list,NULL , NULL ) ;
	if( func == NULL )
		return NULL;
	
	return obj ;
}

